package core

import (
	"encoding/json"
	"github.com/gorilla/websocket"
	"strings"
	"sync"
	"time"
)

const (
	ConnOk   int = 1
	Hearteat int = 2
)

// MessageVo 消息体
type MessageVo struct {
	MsgType int    `json:"msgType"`
	Data    string `json:"data"`
}

// https://blog.51cto.com/u_3409716/5573614
var WS = &Websocket{
	clientMap: make(map[int64]*WsNode, 0),
	lock:      sync.RWMutex{},
}

type WsNode struct {
	userId int64
	ws     *Websocket
	Conn   *websocket.Conn
	Queue  chan []byte
	time   time.Time
	timer  *time.Timer
}

func isClose(msg string) bool {
	return strings.Contains(msg, "websocket: close")
}

func (w *WsNode) close() {
	w.timer.Stop()
	w.Conn.Close()
}

func (w *WsNode) heartbeat() {
	for {
		select {
		case <-w.timer.C:
			// TODO
			if time.Now().Sub(w.time).Seconds() > 60 {
				Log.Warn("当前连接超过60S没有收到客户端的心跳包")
				w.ws.Remove(w.userId)
				return
			}
			w.timer.Reset(10 * time.Second)
		}
	}
}

func (w *WsNode) readData() {
	var (
		msg     []byte
		msgData MessageVo
		err     error
	)
	for {
		if _, msg, err = w.Conn.ReadMessage(); err != nil {
			if isClose(err.Error()) {
				Log.Info("客户端关闭 => %s", err.Error())
				w.ws.Remove(w.userId)
				return
			}
			Log.Error("读取消息失败 => %s", err.Error())
			return
		}
		Log.Info("读取数据 => %s", string(msg))
		if err = json.Unmarshal(msg, &msgData); err != nil {
			Log.Info("解析数据格式错误 => %s", string(msg))
		}
		switch msgData.MsgType {
		case ConnOk:
			Log.Info("连接成功 => %s", msgData.Data)
		case Hearteat:
			w.time = time.Now()
		}
	}
}

func (w *WsNode) writeData() {
	for {
		select {
		case data := <-w.Queue:
			if err := w.Conn.WriteMessage(websocket.TextMessage, data); err != nil {
				Log.Error("发送消息失败 => %s", err.Error())
				break
			}
		}
	}
}

type Websocket struct {
	clientMap map[int64]*WsNode
	lock      sync.RWMutex
}

// AddClient 创建连接
func (w *Websocket) AddClient(userId int64, conn *websocket.Conn) {
	node := &WsNode{
		userId: userId,
		ws:     w,
		Conn:   conn,
		Queue:  make(chan []byte, 50),
		time:   time.Now(),
		timer:  time.NewTimer(5 * time.Second),
	}
	w.lock.Lock()
	w.clientMap[userId] = node
	w.lock.Unlock()
	w.SendMsg(userId, []byte("ok"))
	go node.readData()
	go node.writeData()
	go node.heartbeat()
}

// SendMsg 发送消息
func (w *Websocket) SendMsg(userId int64, msg []byte) {
	w.lock.Lock()
	node, ok := w.clientMap[userId]
	w.lock.Unlock()
	if ok {
		node.Queue <- msg
	}
}

// Remove 移除客户端链接
func (w *Websocket) Remove(userId int64) {
	w.lock.Lock()
	if item, ok := w.clientMap[userId]; ok {
		item.close()
		delete(w.clientMap, userId)
	}
	w.lock.Unlock()
}
